import lombok.Data;
import core.io.RequestProcessor;
import core.server.HttpServlet;
import core.utils.WebClassLoader;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author wsz
 * @date 2021/11/12 17:16
 **/
@Data
public class Bootstrap {

    public static void main(String[] args) throws IOException {
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.start();
    }

    public void start() throws IOException {
        loadServerXml();
        ThreadPoolExecutor executor = initExecutor();

        ServerSocket serverSocket = new ServerSocket(port);
        while (true) {
            Socket socket = serverSocket.accept();
            // TODO 暂定使用转换后的Map，直接获取
            executor.execute(new RequestProcessor(appBase, socket, convertHttpServlet()));
        }
    }

    private int port = 8080;
    private String appBase = "webapps";
    /**
     * 应用与servlet映射关系
     *  demo1 - {urlPattern1-HttpServlet1, urlPattern2-HttpServlet2}
     *  demo2 - {urlPattern1-HttpServlet1, urlPattern2-HttpServlet2}
     */
    private Map<String, Map<String, HttpServlet>> webAppsMap = new HashMap<>();

    /**
     * 1.加载server.xml，读取配置信息
     */
    private void loadServerXml() {
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("server.xml");
        SAXReader reader = new SAXReader();
        try {
            Document document = reader.read(resourceAsStream);
            Element rootElement = document.getRootElement();

            List<Element> serviceList = rootElement.selectNodes("//service");
            for (Element service : serviceList) {
                // TODO 仅模拟一个service
                String name = service.attributeValue("name");

                Element executor = (Element) service.selectSingleNode("//Executor");
                this.corePoolSize = Integer.parseInt(executor.attributeValue("corePoolSize"));

                Element connector = (Element) service.selectSingleNode("//Connector");
                this.port = Integer.parseInt(connector.attributeValue("port"));

                Element engine = (Element) service.selectSingleNode("//Engine");
                Element host = (Element) engine.selectSingleNode("//Host");
                this.appBase = host.attributeValue("appBase");
                loadWebapps(appBase.replaceAll("\\\\", "/"));

            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 2. server.xml中配置的appBase文件
     * @param webappsPath
     * @return
     */
    private void loadWebapps(String webappsPath) {
        File rootFile = new File(webappsPath);
        if (rootFile.exists() && !rootFile.isFile() ) {
            File[] listFiles = rootFile.listFiles();
            for (File file: listFiles) {
                String webPath = file.getPath();
                String contentName = webPath.replace(rootFile.getPath() + "\\", "");

                File webXml = new File(webPath + "\\web.xml");
                // 读取单个xml
                Map<String, HttpServlet> httpServletMap = loadWebXml(webPath.replaceAll("\\\\", "/"), webXml);
                webAppsMap.put(contentName, httpServletMap);
            }
        }
    }


    /**
     * 3.读取各个项目中的web.xml
     * @param packagePath
     * @param webXml
     * @return
     */
    private Map<String, HttpServlet> loadWebXml(String packagePath, File webXml) {
        Map<String, HttpServlet> singleServletMap = new HashMap<>();
        try {
            FileInputStream inputStream = new FileInputStream(webXml);
            SAXReader reader = new SAXReader();

            Document document = reader.read(inputStream);
            Element rootElement = document.getRootElement();

            List<Element> selectNodes = rootElement.selectNodes("//servlet");
            for (Element element : selectNodes) {
                Element servletNameElement = (Element) element.selectSingleNode("servlet-name");
                String servletName = servletNameElement.getStringValue();

                Element servletClassElement = (Element) element.selectSingleNode("servlet-class");
                String servletClass = servletClassElement.getStringValue();

                Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");

                String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();

                // 获取对应HttpServet实现类
                WebClassLoader webClassLoader = new WebClassLoader(packagePath + "/");
                Class<?> aClass = webClassLoader.findClass(servletClass);
                singleServletMap.put(urlPattern, (HttpServlet) aClass.getClassLoader().loadClass(servletClass).newInstance());
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return singleServletMap;
    }
    private int corePoolSize = 5;


    /**
     * 4.线程池
     * @return
     */
    private ThreadPoolExecutor initExecutor() {
        return new ThreadPoolExecutor(
                corePoolSize,
                20,
                100L,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(50),
                new ThreadPoolExecutor.AbortPolicy()
        );
    }

    private Map<String, HttpServlet> convertHttpServlet() {
        Map<String, HttpServlet> allMap = new HashMap<>();
        webAppsMap.forEach((key, value) -> {
            allMap.putAll(value);
        });
        return allMap;
    }

    private Map<String, HttpServlet> servletMap = new HashMap<>();
    /**
     * 加载web.xml中的servlet对应关系
     */
    private void loadServlet() {
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("web.xml");
        SAXReader reader = new SAXReader();

        try {
            Document document = reader.read(resourceAsStream);
            Element rootElement = document.getRootElement();

            List<Element> selectNodes = rootElement.selectNodes("//servlet");
            for (Element element : selectNodes) {
                Element servletNameElement = (Element) element.selectSingleNode("servlet-name");
                String servletName = servletNameElement.getStringValue();

                Element servletClassElement = (Element) element.selectSingleNode("servlet-class");
                String servletClass = servletClassElement.getStringValue();

                Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");

                String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();

                servletMap.put(urlPattern, (HttpServlet) Class.forName(servletClass).newInstance());
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }

    }
}
